package persistence;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import com.mchange.v2.c3p0.ComboPooledDataSource;


public class PersistenceSession {
	private PersistenceSessionFactory psf;
	private PersistenceConfiguration pc;
	private Connection dbcon;
	
	public PersistenceSession(PersistenceSessionFactory psf) throws PersistenceException {
		this.psf = psf;
		this.pc = psf.getPersistenceConfiguration();
		
		/*
		try {
			ConnectionConfiguration cc = psf.getPersistenceConfiguration().getConnectionConfiguration();
			this.dbcon = DriverManager.getConnection(cc.getUrl(), cc.getUserName(), cc.getPassword());
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't open JDBC connection.");
		}
		*/
		
		try {
			ConnectionConfiguration cc = psf.getPersistenceConfiguration().getConnectionConfiguration();
			ComboPooledDataSource cpds = new ComboPooledDataSource();
	        cpds.setDriverClass(cc.getDriverClass());
	        cpds.setJdbcUrl(cc.getUrl());
	        cpds.setUser(cc.getUserName());
	        cpds.setPassword(cc.getPassword());
	        cpds.setMaxStatements(10);
	        dbcon = cpds.getConnection();
		}
		catch (Exception e) {
			throw new PersistenceException("Error getting Connection" + e.getMessage());
		}
	}
	
	public void createTable(Class<?> clazz) throws PersistenceException {
		PersistentClass pc = this.pc.getPersistentClass(clazz);
		try {
			Statement s = this.dbcon.createStatement();
			//System.out.println(pc.getCreateTableStatement());
			s.execute(pc.getCreateTableStatement());
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't execute CREATE statement.");
		}
	}
	
	public void dropTable(Class<?> clazz) throws PersistenceException {
		PersistentClass pc = this.pc.getPersistentClass(clazz);
		try {
			Statement s = this.dbcon.createStatement();
			s.execute(pc.getDropTableStatement());
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't execute DROP statement.");
		}
	}
	
	public void save(Object bean) throws PersistenceException {
		PersistentClass pc = this.pc.getPersistentClass(bean.getClass());
		try {
			PreparedStatement ps = this.dbcon.prepareStatement(pc.getInsertStatement());
			//System.out.println(pc.getInsertStatement());
			int i = 1;
			for(PersistentProperty pp : pc.getPersistentProperties()) {
				if(pp.isId()) {
					continue;
				}
				Object o = pp.getValue(bean);
				if(o == null) {
					ps.setNull(i, Types.BIGINT);
				}
				else {
					ps.setObject(i, o);
					//System.out.println(i + ": " + o);
				}
				i++;
			}
			int newId = ps.executeUpdate();
			pc.getId().setValue(bean, newId);
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't execute INSERT statement.");
		}
	}
	
	public Object get(Class<?> clazz, Object id) throws PersistenceException {
		PersistentClass pc = this.pc.getPersistentClass(clazz);
		try {
			PreparedStatement ps = this.dbcon.prepareStatement(pc.getSelectByIdStatement());
			ps.setObject(1, id);
			ResultSet rs = ps.executeQuery();
		    if(rs.next()) {
		    	Object bean = pc.createInstance();
		    	int i = 1;
		    	for (PersistentProperty pp : pc.getPersistentProperties()) {
		    		pp.setValue(bean, rs.getObject(i));
		    		i++;
		        }
		    	return bean;
		    }
		    return null;
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't execute SELECT statement.");
		}
	}
	
	public void update(Object bean) throws PersistenceException {
		/*
		PersistentClass pc = this.pc.getPersistentClass(bean.getClass());
		try {
			int i = 1;
			PreparedStatement ps = this.dbcon.prepareStatement(pc.getUpdateStatement());
			for(PersistentProperty pp : pc.getPersistentProperties()) {
				if(!pp.isId()) {
					ps.setObject(i, pp.);
					i++;
				}
			}
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't execute UPDATE statement.");
		}
		*/
	}
	
	public void delete(Object bean) throws PersistenceException {
		PersistentClass pc = this.pc.getPersistentClass(bean.getClass());
		try {
			Statement s = this.dbcon.createStatement();
			s.execute(pc.getDeleteStatement());
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't execute DELETE statement.");
		}
	}
	
	public void close() throws PersistenceException {
		try {
			dbcon.close();
		}
		catch(SQLException e) {
			throw new PersistenceException("Can't close JDBC connection.");
		}
	}
	
	public PersistenceConfiguration getConnectionConfiguration() {
		return this.pc;
	}

	public List<?> query(Class<?> class1, String string) throws SQLException, PersistenceException {
		PersistentClass c = this.pc.getPersistentClass(class1);
				
		Statement s = this.dbcon.createStatement();
		ResultSet rs = s.executeQuery(string);
		List result = new ArrayList();
		
		while(rs.next()) {
			Object o = c.createInstance();
			int i = 1;
			
			for(PersistentProperty pp : c.getPersistentProperties()) {
				pp.setValue(o,rs.getObject(i));
				i++;
			}
			
			result.add(o);
		}
	
		return result;
	}
}
